package rexsee.core.utilities;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.sql.Date;
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Random;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;

import rexsee.core.application.RexseeApplication;
import rexsee.core.browser.Browser;
import rexsee.core.browser.RexseeUrl;
import rexsee.core.browser.clazz.DomainWhiteList;
import rexsee.core.browser.clazz.LogListeners;
import rexsee.core.browser.clazz.LogListeners.LogListener;
import rexsee.core.transportation.PageSaver;
import rexsee.core.widget.Div;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AsyncPlayer;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Environment;
import android.webkit.MimeTypeMap;

public class Utilities {

	public static final void open(Context context, String url) {

		if (url == null || url.compareTo("") == 0) return;
		Uri uri = Uri.parse(url);
		String scheme = uri.getScheme();
		if (scheme == null) return;

		DomainWhiteList domainWhiteList = new DomainWhiteList(context);
		domainWhiteList.inherit();

		try {
			if (scheme.equals("tel")) {
				Intent i = new Intent();
				i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
				i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				i.setAction(Intent.ACTION_DIAL);
				i.setData(uri);
				context.startActivity(i);
			} else if (scheme.equals("wtai")) {
				Intent i = new Intent();
				i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
				i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				i.setAction(Intent.ACTION_DIAL);
				String newUrl = "tel:" + url.substring(url.indexOf(";") + 1).trim().replace(" ", "").replace("-", "").replace("_", "");
				i.setData(Uri.parse(newUrl));
				context.startActivity(i);
			} else if (scheme.equals("mailto")) {
				Intent i = new Intent();
				i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
				i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				i.setAction(Intent.ACTION_SENDTO);
				i.setData(uri);
				context.startActivity(i);
			} else if (scheme.equals("smsto")) {
				Intent i = new Intent();
				i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
				i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				i.setAction(Intent.ACTION_SENDTO);
				i.setData(uri);
				context.startActivity(i);
			} else if (scheme.equals("sms")) {
				Intent i = new Intent();
				i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
				i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				i.setAction(Intent.ACTION_SENDTO);
				i.setData(Uri.parse("smsto" + url.substring(url.indexOf(":"))));
				context.startActivity(i);
			} else if (scheme.equals("file")) {
				String lUrl = url.toLowerCase();
				if (new File(uri.getSchemeSpecificPart()).isDirectory()
						|| lUrl.endsWith("html")
						|| lUrl.endsWith("htm")
						|| lUrl.endsWith("xml")
						|| lUrl.endsWith("smil")
						|| lUrl.endsWith("jpg")
						|| lUrl.endsWith("jpeg")
						|| lUrl.endsWith("png")
						|| lUrl.endsWith("gif")
						|| lUrl.endsWith("txt")) {
					startMe(context, url);
				} else {
					try {
						Intent i = new Intent();
						i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
						i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						i.setAction(Intent.ACTION_VIEW);
						i.setDataAndType(uri, Utilities.getMime(url));
						context.startActivity(i);
					} catch (Exception e) {
					}
				}
			} else if (scheme.equals("http") || scheme.equals("https")) {
				if (domainWhiteList != null && domainWhiteList.isValidUrl(url)) {
					startMe(context, url);
				} else {
					Intent i = new Intent();
					i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
					i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
					i.setAction(Intent.ACTION_VIEW);
					i.setData(uri);
					context.startActivity(i);
				}
			} else {
				startMe(context, url);
			}
		} catch (Exception e) {
		}
	}
	public static final void downloadByDefault(Browser browser, String url) {
		url = browser.urlListeners.getAbsoluteUrl(url);
		Uri uri = Uri.parse(url);
		String scheme = uri.getScheme();
		if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) {
			browser.exception("Download", url + ":Invalid url.");
		} else {
			Intent i = new Intent();
			i.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
			i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			i.setAction(Intent.ACTION_VIEW);
			i.setData(uri);
			browser.getContext().startActivity(i);
		}
	}
	public static final void startMe(Context context, String url) {
		Intent intent = getStartMeIntent(context, url);
		if (intent == null) return;
		context.startActivity(intent);
	}
	public static final Intent getStartMeIntent(Context context, String url) {
		try {
			PackageManager pm = context.getPackageManager();
			String packageName = context.getPackageName();
			PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES | PackageManager.GET_PERMISSIONS | PackageManager.GET_URI_PERMISSION_PATTERNS | PackageManager.GET_UNINSTALLED_PACKAGES);
			ComponentName component = new ComponentName(packageName, info.activities[0].name);
			Intent i = new Intent();
			i.setComponent(component);
			i.setData(Uri.parse(url));
			i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			return i;
		} catch (Exception e) {
			return null;
		}
	}
	public static final String timeStamp2dateString(long timeStamp) {
		Date date = new Date(timeStamp);
		return (new SimpleDateFormat("yyyy-M-d E H:m:s")).format(date);
	}
	public static final Bitmap getBitmap(String path) {
		File file = prepareReadFile(path);
		if (file == null) return null;
		try {
			return BitmapFactory.decodeFile(file.getAbsolutePath());
		} catch (Exception e) {
			return null;
		}
	}
	public static final Bitmap getBitmap(Browser browser, Div div) {
		if (div == null) {
			if (browser != null) browser.exception("Get Bitmap", "Null div.");
			return null;
		}
		try {
			div.setDrawingCacheEnabled(true);
			div.destroyDrawingCache();
			Bitmap bitmap = div.getDrawingCache();
			if (bitmap == null) {
				if (browser != null) browser.exception("Get Bitmap", "Null bitmap.");
			}
			return bitmap;
		} catch (Exception e) {
			if (browser != null) browser.exception("Get Bitmap", e.getLocalizedMessage());
			return null;
		}
	}
	public static final void playSound(Context context, String sound) {
		try {
			Uri uri = Uri.parse(sound);
			if (uri == null) return;
			String scheme = uri.getScheme();
			if (scheme == null) return;
			if (scheme.equals("http") || scheme.equals("https") || scheme.equals("rtsp") || scheme.equals("file")) {
				new AsyncPlayer("").play(context, uri, false, AudioManager.STREAM_MUSIC);
			}
		} catch (Exception e) {
		}
	}
	public static String getAttribute(String html, String attributeName) {
		html = html.trim();
		int pos1 = html.lastIndexOf(attributeName.toLowerCase() + "=");
		int pos2 = html.lastIndexOf(attributeName.toUpperCase() + "=");
		int pos = Math.max(pos1, pos2);
		if (pos < 0) return null;
		html = html.substring(pos + attributeName.length() + 1);
		if (html.startsWith("'")) {
			html = html.substring(1);
			return html.substring(0, html.indexOf("'"));
		} else if (html.startsWith("\"")) {
			html = html.substring(1);
			return html.substring(0, html.indexOf("\""));
		} else {
			int i1 = html.indexOf(" ");
			int i2 = html.indexOf(">");
			int index;
			if (i1 < 0 && i2 < 0) index = -1;
			else if (i1 < 0 && i2 > 0) index = i2;
			else if (i1 > 0 && i2 < 0) index = i1;
			else index = Math.min(i1, i2);
			return (index < 0) ? null : html.substring(0, index);
		}
	}

	public static String getExtension(String url) {
		if (url == null) return "";
		url = PageSaver.getCleanedUrl(url);
		String name = url.substring(url.lastIndexOf("/") + 1);
		return (name.contains(".")) ? name.substring(name.lastIndexOf(".") + 1).toLowerCase() : "";
	}
	public static String getMime(String url) {
		String ext = getExtension(url);
		if (ext.equals("")) {
			return null;
		} else if (ext.equalsIgnoreCase("3ga")) {
			return "audio/mp4";
		} else {
			return MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl("xxx." + ext));
		}
	}

	public static int getInt(String str, int defaultValue) {
		if (str == null) return defaultValue;
		try {
			str = str.toLowerCase().trim().replaceAll("px", "");
			return Integer.parseInt(str);
		} catch (Exception e) {
			return defaultValue;
		}
	}
	public static int[] getIntArray(String value, int defaultValue) {
		if (value == null) return null;
		String[] tmp = value.toLowerCase().replace(" ", "").replace("px", "").split(",");
		int[] rtn = new int[tmp.length];
		for (int i = 0; i < tmp.length; i++) {
			rtn[i] = Utilities.getInt(tmp[i], defaultValue);
		}
		return rtn;
	}
	public static int[] getIntArray(String value, int number, int defaultValue) {
		if (value == null) return null;
		String[] tmp = value.toLowerCase().replace(" ", "").replace("px", "").split(",");
		if (tmp.length != number) return null;
		int[] rtn = new int[number];
		for (int i = 0; i < number; i++) {
			rtn[i] = Utilities.getInt(tmp[i], defaultValue);
		}
		return rtn;
	}
	public static double getDouble(String str, double defaultValue) {
		if (str == null) return defaultValue;
		try {
			str = str.toLowerCase().trim().replaceAll("px", "");
			if (str.endsWith("%")) {
				return Double.parseDouble(str.substring(0, str.length() - 1)) / 100;
			} else {
				return Double.parseDouble(str);
			}
		} catch (Exception e) {
			return defaultValue;
		}
	}
	public static float getFloat(String str, float defaultValue) {
		if (str == null) return defaultValue;
		try {
			str = str.toLowerCase().trim().replaceAll("px", "");
			if (str.endsWith("%")) {
				return Float.parseFloat(str.substring(0, str.length() - 1)) / 100;
			} else {
				return Float.parseFloat(str);
			}
		} catch (Exception e) {
			return defaultValue;
		}
	}
	public static float[] getFloatArray(String value, float defaultValue) {
		if (value == null) return null;
		String[] tmp = value.toLowerCase().replace(" ", "").replace("px", "").split(",");
		float[] rtn = new float[tmp.length];
		for (int i = 0; i < tmp.length; i++) {
			rtn[i] = Utilities.getFloat(tmp[i], defaultValue);
		}
		return rtn;
	}
	public static float[] getFloatArray(String value, int number, float defaultValue) {
		if (value == null) return null;
		String[] tmp = value.toLowerCase().replace(" ", "").replace("px", "").split(",");
		if (tmp.length != number) return null;
		float[] rtn = new float[number];
		for (int i = 0; i < number; i++) {
			rtn[i] = Utilities.getFloat(tmp[i], defaultValue);
		}
		return rtn;
	}
	public static long getLong(String str, long defaultValue) {
		if (str == null) return defaultValue;
		try {
			str = str.toLowerCase().trim().replaceAll("px", "");
			return Long.parseLong(str);
		} catch (Exception e) {
			return defaultValue;
		}
	}
	public static boolean getBoolean(String str, boolean defaultValue) {
		if (str == null) return defaultValue;
		try {
			return Boolean.getBoolean(str);
		} catch (Exception e) {
			return defaultValue;
		}
	}

	public static String md5file(String path) {
		byte[] bytes = Utilities.getContent(path);
		if (bytes == null) return "";
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(bytes);
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer("");
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0) i += 256;
				if (i < 16) buf.append("0");
				buf.append(Integer.toHexString(i));
			}
			return buf.toString();
		} catch (Exception e) {
			return "";
		}
	}
	public static String md5(String plainText, boolean is32) {
		if (plainText == null) return "";
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(plainText.getBytes());
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer("");
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0) i += 256;
				if (i < 16) buf.append("0");
				buf.append(Integer.toHexString(i));
			}
			return (is32) ? buf.toString() : buf.toString().substring(8, 24);
		} catch (Exception e) {
			return "";
		}
	}
	public static String getRandom(int len) {
		String returnStr = "";
		char[] ch = new char[len];
		Random rd = new Random();
		for (int i = 0; i < len; i++) {
			ch[i] = (char) (rd.nextInt(9) + 97);
		}
		returnStr = new String(ch);
		return returnStr;
	}
	public static String getHmacSHA1Signature(String source, String key) {
		try {
			Mac mac = Mac.getInstance("HmacSHA1");
			SecretKeySpec spec = new SecretKeySpec(key.getBytes(), "HmacSHA1");
			mac.init(spec);
			byte[] byteHMAC = mac.doFinal(source.getBytes());
			return Base64.encodeBytes(byteHMAC);
		} catch (Exception e) {
			return RexseeApplication.EXCEPTION_PREFIX + e.getLocalizedMessage();
		}
	}

	public static byte[] getContent(String path) {
		File file = prepareReadFile(path);
		if (file == null) return null;
		try {
			BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
			byte[] b = new byte[buf.available()];
			buf.read(b);
			buf.close();
			return b;
		} catch (Exception e) {
			return null;
		}
	}
	public static File prepareReadFile(String path) {
		if (path == null || path.trim().equals("")) return null;
		if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) return null;
		if (!path.toLowerCase().startsWith("file://")) return null;
		File file = new File(Uri.parse(path).getPath());
		return (file.exists() && file.isFile() && file.canRead()) ? file : null;
	}
	public static File prepareWriteFile(String path) {
		if (path == null || path.trim().equals("")) return null;
		if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) return null;
		if (!path.toLowerCase().startsWith("file://")) return null;
		File file = new File(Uri.parse(path).getPath());
		if (!file.exists()) {
			File parent = file.getParentFile();
			if (!parent.exists()) {
				return (parent.mkdirs()) ? file : null;
			} else {
				return (parent.isDirectory() && parent.canWrite()) ? file : null;
			}
		} else {
			return (file.isFile() && file.canWrite()) ? file : null;
		}
	}
	public static boolean putContent(String path, byte[] bytes) {
		if (bytes == null) return false;
		File file = prepareWriteFile(path);
		if (file == null) return false;
		try {
			FileOutputStream fileOutput = new FileOutputStream(file);
			fileOutput.write(bytes);
			fileOutput.close();
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	public static boolean copy(String path, String newPath) {

		File file = Utilities.prepareReadFile(path);
		File newFile = Utilities.prepareWriteFile(newPath);

		if (file == null || newFile == null) return false;

		try {

			FileInputStream in = new FileInputStream(file);
			FileOutputStream out = new FileOutputStream(newFile);

			byte buffer[] = new byte[1024 * 1024];
			int realLength;
			while ((realLength = in.read(buffer)) > 0) {
				out.write(buffer, 0, realLength);
			}

			in.close();
			out.close();

			return true;

		} catch (Exception e) {
			return false;
		}
	}

	public static byte[] getAssetContent(Context context, String path) {
		if (path.startsWith(RexseeUrl.ASSET_URL)) path = path.replace(RexseeUrl.ASSET_URL, "");
		AssetManager am = context.getResources().getAssets();
		try {
			InputStream in = am.open(path);
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			if (in == null) return null;
			byte[] buffer = new byte[1024];
			int len = in.read(buffer);
			while (len >= 0) {
				out.write(buffer, 0, len);
				len = in.read(buffer);
			}
			in.close();
			return out.toByteArray();
		} catch (IOException e) {
			return null;
		}
	}
	public static boolean copyAssetToExternalStorage(Context context, String assetPath, String sdPath) {
		if (!(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))) return false;
		byte[] bytes = getAssetContent(context, assetPath);
		if (bytes == null) return false;
		return Utilities.putContent(sdPath, bytes);
	}

	public static HashMap<String, String> string2map(String string) {
		return string2map(string, ";", ":", true);
	}
	public static HashMap<String, String> string2map(String string, String partSplit, String assignSplit, boolean cleanKey) {
		if (string == null || string.trim().equals("")) return null;
		HashMap<String, String> rtn = new HashMap<String, String>();
		String[] argus = string.split(partSplit);
		for (int i = 0; i < argus.length; i++) {
			if (argus[i].indexOf(assignSplit) <= 0) continue;
			String key = argus[i].substring(0, argus[i].indexOf(assignSplit));
			if (cleanKey) key = key.trim().toLowerCase().replaceAll("\\-", "\\_");
			String value = argus[i].substring(argus[i].indexOf(assignSplit) + 1).trim();
			rtn.put(key, value);
		}
		return rtn;
	}

	public static void scanMediaStore(Context context) {
		context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
	}
	public static void scanMediaStore(Context context, String fileName) {
		context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(fileName)));
	}

	public static byte[] getBytes(Context context, String url, LogListener logListener) {
		try {
			HttpGet post = new HttpGet(url);
			DefaultHttpClient httpClient = new DefaultHttpClient();
			HttpParams params = httpClient.getParams();
			HttpProtocolParams.setUserAgent(params, RexseeApplication.getUserAgent(context));
			HttpResponse response = httpClient.execute(post);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				return EntityUtils.toByteArray(response.getEntity());
			} else {
				if (logListener != null) logListener.run(context, null, LogListeners.LOG_TYPE_EXCEPTION, "getBytes: statusCode is " + statusCode);
				return null;
			}
		} catch (Exception e) {
			if (logListener != null) logListener.run(context, null, LogListeners.LOG_TYPE_EXCEPTION, "getBytes: " + e.getLocalizedMessage());
			return null;
		}
	}
	public static String getContent(Context context, String url, LogListener logListener) {
		byte[] bytes = getBytes(context, url, logListener);
		if (bytes == null) return null;
		String result = new String(bytes);
		String charset = Utilities.guessHtmlCharset(result);
		try {
			result = new String(bytes, charset);
		} catch (Exception e) {
			if (logListener != null) logListener.run(context, null, LogListeners.LOG_TYPE_EXCEPTION, "getContent: " + e.getLocalizedMessage());
		}
		return result;
	}
	public static String getContent(Context context, String url, LogListener logListener, String encoding) {
		byte[] bytes = Utilities.getBytes(context, url, logListener);
		if (bytes == null) return null;
		try {
			return new String(bytes, encoding);
		} catch (Exception e) {
			return null;
		}
	}
	public static String guessHtmlCharset(String src) {
		String tmp = src.toLowerCase();
		if (!tmp.contains("charset=")) { return "UTF-8"; }
		tmp = tmp.split("charset=")[1];
		while (tmp.startsWith(" ") || tmp.startsWith("'") || tmp.startsWith("\""))
			tmp = tmp.substring(1);
		String charset = "";
		while (!(tmp.startsWith(" ") || tmp.startsWith("'") || tmp.startsWith("\"") || tmp.startsWith(">"))) {
			charset = charset + tmp.substring(0, 1);
			tmp = tmp.substring(1);
		}
		return (charset.equals("")) ? "UTF-8" : charset.toUpperCase();
	}

	public static String getDateAndTime() {
		long timestamp = System.currentTimeMillis();
		return new Date(timestamp).toString() + " " + new Time(timestamp).toString();
	}
	public static String getDate() {
		long timestamp = System.currentTimeMillis();
		return new Date(timestamp).toString();
	}
	public static String getTime() {
		long timestamp = System.currentTimeMillis();
		return new Time(timestamp).toString();
	}
	public static int px2int(String px, int defaultValue, float density) {
		if (px == null) return defaultValue;
		px = px.toLowerCase().replace("px", "").replace(" ", "");
		int rtn = Utilities.getInt(px, -99999);
		if (rtn == -99999) return defaultValue;
		else return Math.round(rtn * density);
	}

	public static class CompratorFileByLastModifiedAsc
			implements
				Comparator<File> {
		@Override
		public int compare(File file1, File file2) {
			long diff = file1.lastModified() - file2.lastModified();
			if (diff > 0) return 1;
			else if (diff == 0) return 0;
			else return -1;
		}
		@Override
		public boolean equals(Object obj) {
			return true;
		}
	}
	public static class CompratorFileByLastModifiedDesc
			implements
				Comparator<File> {
		@Override
		public int compare(File file1, File file2) {
			long diff = file1.lastModified() - file2.lastModified();
			if (diff > 0) return -1;
			else if (diff == 0) return 0;
			else return 1;
		}
		@Override
		public boolean equals(Object obj) {
			return true;
		}
	}
	public static class CompratorFileBySizeAsc implements Comparator<File> {
		@Override
		public int compare(File file1, File file2) {
			long diff = file1.length() - file2.length();
			if (diff > 0) return 1;
			else if (diff == 0) return 0;
			else return -1;
		}

		@Override
		public boolean equals(Object obj) {
			return true;
		}
	}
	public static class CompratorFileBySizeDesc implements Comparator<File> {
		@Override
		public int compare(File file1, File file2) {
			long diff = file1.length() - file2.length();
			if (diff > 0) return -1;
			else if (diff == 0) return 0;
			else return 1;
		}

		@Override
		public boolean equals(Object obj) {
			return true;
		}
	}

	public static abstract class CacheRunnable {
		public abstract void run(String path);
	}

}
